package com.khaled.protclass.model.feature.blast;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Random;

import org.apache.log4j.Logger;

import com.khaled.protclass.model.base.BaseFeature;
import com.khaled.protclass.model.base.IProtein;
import com.khaled.protclass.model.bean.weka.Attribute;
import com.khaled.protclass.model.bean.weka.WekaDataTypes;
import com.khaled.protclass.model.feature.composition.AminoAcidComposition;
import com.khaled.protclass.model.util.UtilAminoAcids;
import com.khaled.protclass.model.util.UtilArray;
import com.khaled.protclass.model.util.UtilDirectory;
import com.khaled.protclass.model.util.UtilNumber;

public class PSSM extends BaseFeature
{
	/** Logger. */
	private static Logger logger = Logger.getLogger(AminoAcidComposition.class);

	public static final String BLAST_PATH = "NCBI-PATH";

	public static final String DB_PATH = "DB-PATH";

	@Override
	public String getName()
	{
		return "PSSM";
	}

	@Override
	public ArrayList<Attribute> getAttributes()
	{
		logger.info("getAttributes starts.");

		ArrayList<Attribute> attributes = new ArrayList<Attribute>();

		for (String amino : UtilAminoAcids.getInstance().getAminoSymbols())
		{
			for (int i = 1; i <= 20; i++)
			{
				attributes.add(new Attribute(amino + i, WekaDataTypes.NUMERIC));
			}
		}
		logger.info("Number of attributes:" + attributes.size());
		logger.info("getAttributes ends.");

		return attributes;
	}

	@Override
	public String getFeatureVector(IProtein protein)
	{
		StringBuffer featureVector = new StringBuffer();

		logger.info("getFeatureVector starts.");
		logger.info("Protein ID: " + protein.getProteinID());
		logger.info("Protein Classification:" + protein.getClassification());
		logger.info("Protein Sequence:" + protein.getProteinSequence());

		try
		{
			String protSequence = protein.getProteinSequence();

			String pssmFilePath = getPSSMFilePath(protein);

			if (!UtilDirectory.exist(pssmFilePath))
			{
				for (int i = 0; i < getAttributes().size(); i++)
				{
					featureVector.append("?").append(",");
				}
				return featureVector.toString();
			}

			Double[][] pssm_Matrix = getPSSM_Matrix(pssmFilePath, protSequence.length());

			logger.info("PSSM Matrix:\n" + pssm_Matrix);

			for (String amino : UtilAminoAcids.getInstance().getAminoSymbols())
			{
				Double[] row = new Double[20];
				UtilArray.initialize(row, 0.0);

				for (int i = 0; i < protSequence.length(); i++)
				{
					String protAmino = Character.toString(protSequence.charAt(i));

					if (amino.equals(protAmino))
					{
						Double[] pssm = pssm_Matrix[i];

						UtilArray.normalize(pssm, true);

						row = UtilArray.sum(row, pssm);
					}
				}

				row = UtilArray.divideBy(row, protSequence.length());

				logger.info("The summation matrix for amino (" + amino + "):" + row);

				featureVector.append(UtilArray.getArrayAsString(row));
			}

		}
		catch (Exception ex)
		{
			System.out.println("Problem in protein: " + protein.getProteinID());
			ex.printStackTrace();
		}
		logger.info("Feature Vector: " + featureVector);

		return featureVector.toString();
	}

	private Double[][] getPSSM_Matrix(String pssmFilePath, int proteinLength)
	{
		try
		{
			Double[][] pssm_Matrix = new Double[proteinLength][20];

			UtilArray.initialize(pssm_Matrix, 0.0);

			ArrayList<String> pssmFileLines = UtilDirectory.readFileLines(pssmFilePath);
			int index = 0;
			for (int i = 3; i < pssmFileLines.size(); i++)
			{
				String line = pssmFileLines.get(i).trim();
				if ("".equals(line))
				{
					continue;
				}
				else if (line.trim().startsWith("K"))
				{
					break;
				}

				String[] elements = line.split(" ");

				int count = 0;
				for (int j = 2; j < elements.length; j++)
				{
					if (count > 19)
					{
						break;
					}
					else if ("".equals(elements[j].trim()))
					{
						continue;
					}
					else
					{
						try
						{
							pssm_Matrix[index][count] = Double.parseDouble(elements[j]);
							count++;
						}
						catch (NumberFormatException ex)
						{
							double[] values = UtilNumber.validateNumber(elements[j]);

							for (int k = 0; k < values.length; k++)
							{
								pssm_Matrix[index][count] = values[k];
								count++;
							}
						}
					}
				}
				index++; // protein index
			}
			return pssm_Matrix;
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		catch (Exception e)
		{
			System.out.println("Error in " + pssmFilePath);
			e.printStackTrace();
		}
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void init()
	{
		// TODO Auto-generated method stub

	}

	public String getPSSMFilePath(IProtein protein) throws Exception
	{
		// 1. Generate file names
		Random r = new Random();
		String protID = "protein" + r.nextInt(1000);
		String inputFile = getProperties().get(BLAST_PATH) + "\\" + protID + ".fasta";
		String pssmFilePath = getProperties().get(BLAST_PATH) + "\\" + protID + ".blast";

		// 2. Create the input fasta file in the workingDicrectory.
		UtilDirectory.createFile(protein.getProteinSequence(), inputFile);

		// 3. Prepare the psiblast command.
		String command = psiblast(getProperties().get(DB_PATH), inputFile, "3", pssmFilePath);

		// 4. Run the command.
		File workDir = new File(getProperties().get(BLAST_PATH));
		Runtime systemShell = Runtime.getRuntime();
		Process shellOutput;
		try
		{
			shellOutput = systemShell.exec(workDir + "\\" + command, null, workDir);

			InputStreamReader isr = new InputStreamReader(shellOutput.getInputStream());
			BufferedReader br = new BufferedReader(isr);

			// Waiting
			while (br.readLine() != null)
			{
				// DO NOTHING
			}
			shellOutput.destroy();
			// 5. remove fasta file
			UtilDirectory.deleteFile(inputFile);
		}
		catch (IOException e)
		{
			logger.error("Error while generating the pssm for protein: " + protein.getProteinSequence());
			logger.error(e);
			// 5. remove fasta file
			UtilDirectory.deleteFile(inputFile);

			throw new Exception(e.getMessage());
		}
		return pssmFilePath;
	}

	public String psiblast(String dbPath, String inputFilePath, String numIterations, String outputFilePath)
	{
		StringBuffer command = new StringBuffer("");

		command.append("psiblast ");
		command.append("-db \"").append(dbPath).append("\\nr.00\" "); // you can
																		// change
																		// the
																		// db
																		// here!
		command.append("-query ").append(" \"").append(inputFilePath).append("\" ");
		command.append("-num_iterations ").append(numIterations).append(" ");
		command.append("-out_ascii_pssm \"").append(outputFilePath).append("\"");

		return command.toString();
	}

}
